/**
 * Project: d20Engine
 * Created: Aug 13, 2006 by bebopJMM
 *------------------------------------------------------------------------------
 * $Id$
 */
package org.rollinitiative.d20.combat;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.rollinitiative.d20.dice.Dice;
import org.rollinitiative.d20.encounter.Encounter;
import org.rollinitiative.d20.entity.Ability;
import org.rollinitiative.d20.entity.Actor;

/**
 * Combatant is an association class, linking an actor with an allegiance for purposes of an
 * encounter.
 * 
 * @author bebopJMM
 * 
 */
public class Combatant implements Comparable<Combatant>
{
    static final Log LOG_ = LogFactory.getLog(Combatant.class);

    private Actor actor_;

    private Allegiance allegiance_;

    private Initiative initiative_;

    private boolean aware_ = true;

    private boolean autonomous_ = true;

    private ActionSet actionSet = null;
    
    private Encounter encounter_;

    Dice dice_ = Dice.getInstance();


    /**
     * @param allegiance
     * @param actor
     */
    public Combatant(Actor actor, Allegiance allegiance)
    {
        this.allegiance_ = allegiance;
        this.actor_ = actor;
        this.initiative_ = new Initiative(actor.getInitiativeMod());
    }


    /**
     * @param actor
     * @param allegiance
     * @param autonomous
     */
    public Combatant(Actor actor, Allegiance allegiance, boolean autonomous)
    {
        this(actor, allegiance);
        this.autonomous_ = autonomous;
    }


    /**
     * @return the actor
     */
    public Actor getActor()
    {
        return actor_;
    }


    /**
     * @param actor_ the actor_ to set
     */
    public void setActor(Actor actor_)
    {
        this.actor_ = actor_;
    }


    /**
     * @return the allegiance_
     */
    public Allegiance getAllegiance()
    {
        return allegiance_;
    }


    /**
     * @param allegiance_ the allegiance_ to set
     */
    public void setAllegiance(Allegiance allegiance_)
    {
        this.allegiance_ = allegiance_;
    }


    /**
     * @return the combatant's current initiative
     * @since incr-0.1
     */
    public Initiative getInitiative()
    {
        return initiative_;
    }


    /**
     * @return true if the combatant has awareness of the encounter, otherwise false.
     * @since incr-0.1
     */
    public boolean isAware()
    {
        return aware_;
    }


    /**
     * @param aware_ Awareness value setting
     * @since incr-0.1
     */
    public void setAware(boolean aware)
    {
        this.aware_ = aware;
    }

    public void setEncounter(Encounter encounter)
    {
        this.encounter_ = encounter;
    }

    /**
     * TODO: Add support for hand-rolled combatants
     * 
     * @return
     * @since incr-0.1
     */
    public Initiative rollInitiative()
    {
        if (autonomous_) {
            initiative_.setBaseRoll(dice_.roll(1, 20, 0));
            return this.initiative_;
        }
        else {
            LOG_.error("Non-autonomous combatants not supported");
            return null;
        }
    }


    /**
     * TODO: Add support for hand-rolled combatants
     * 
     * @return
     * @since incr-0.1
     */
    public Initiative rollInitiativeTiebreak()
    {
        if (autonomous_) {
            initiative_.setTieRoll(dice_.roll(1, 100, 0));
            return this.initiative_;
        }
        else {
            LOG_.error("Non-autonomous combatants not supported");
            return null;
        }
    }


    public void setActionSet(ActionSet set)
    {
        actionSet = set;
    }


    public ActionSet getActionSet()
    {
        // return new ActionSet();
        return actionSet;
    }


    /**
     * 
     * @see http://www.d20srd.org/srd/combat/combatStatistics.htm 
     * TODO ensure attack is 'consumed'
     */
    public int meleeAttack(int attackNum)
    {
        int baseRoll = dice_.roll(1, 20, 0);
        int bab = actor_.getAttacks().getMeleeModifier().getCurrent();
        int mods = bab +
                + Ability.getModifier(actor_.getAbility(Ability.STR).getCurrent())
                + actor_.getCurrentSize().getCombatModifier();
        LOG_.info("Attack roll = " + baseRoll + " + " + mods + " = " + (baseRoll + mods));
        return baseRoll + mods;
    }


    public int meleeAttack()
    {
        int baseRoll = dice_.roll(1, 20, 0);
        int mods = actor_.getAttacks().getMeleeModifier().getCurrent();
        LOG_.debug("Attack roll (base + mods) = " + (baseRoll + mods) + "(" + baseRoll + " + "
                + mods + " )");
        return baseRoll + mods;
    }


    public int defence()
    {
        return actor_.getDefense().defend(false);
//        int baseRoll = dice_.roll(1, 20, 0);
//        int mods = Ability.getModifier(actor_.getAbility(Ability.DEX).getCurrent());
//        LOG_.info("Defense roll = " + baseRoll + " + " + mods + " = " + (baseRoll + mods));
//        return baseRoll + mods;
    }


    /**
     * The initiative value is used to evaluate the comparison. Order runs highest to lowest.
     * 
     * @see java.lang.Comparable#compareTo(java.lang.Object)
     */
    public int compareTo(Combatant o)
    {
        if (o.initiative_.getValue() == this.initiative_.getValue()) {
            if (o.initiative_.getModifier() == this.initiative_.getModifier()) {
                if (o.initiative_.getTieRoll() == this.initiative_.getTieRoll()) {
                    return 0;
                }
                else {
                    return o.initiative_.getTieRoll() - this.initiative_.getTieRoll();
                }
            }
            else {
                return o.initiative_.getModifier() - this.initiative_.getModifier();
            }
        }
        else {
            return o.initiative_.getValue() - this.initiative_.getValue();
        }
    }


    /**
     * (non-Javadoc)
     * 
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object arg0)
    {
        if (arg0 instanceof Combatant) {
            Combatant c = (Combatant) arg0;
            return this.actor_.equals(c.actor_);
        }
        else
            return false;
    }

}
